package utils.fileConvert;

import annotate.DBF;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.linuxense.javadbf.*;
import com.lkx.util.Excel;
import entity.DbfParam;
import lombok.extern.slf4j.Slf4j;
import utils.object.UtilValidate;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * DBF 工具类的实现方法类
 *
 * @author Chen Yuan
 * @create 2021-12-22  14:34
 */
@Slf4j
public class DBFUtilBase {

	/**
	 * 读取 DBF 文件的基本方法
	 *
	 * @param param 入参对象
	 * @param clazz 需要的结果对象
	 * @return 读取数据的结果列表
	 * @throws Exception 读取文件、获取文件流的相关异常
	 */
	public static <T> List<T> commonReadDbf(DbfParam param, Class<T> clazz) throws Exception {
		ArrayList<T> result = new ArrayList<>();
		InputStream inputStream = null;
		// 获取 表头名称 和 字段名称 映射关系
		Map<String, String> columnMap = param.getColumnMap();
		if (UtilValidate.isEmpty(columnMap)) {
			columnMap = getColumnMap(clazz, 1);
		}
		// 获得输入流
		if (!param.getIsStream()) {
			String filePath = param.getFilePath();
			if (UtilValidate.isEmpty(filePath)) {
				return result;
			}
			String fileType = filePath.substring(filePath.lastIndexOf(".") + 1, filePath.length()).toLowerCase();
			if (!Objects.equals(fileType, "dbf")) {
				throw new Exception("传入文件格式异常");
			}
			inputStream = new FileInputStream(filePath);
		} else {
			inputStream = new ByteArrayInputStream(param.getBuf());
		}
		try {

			//封装DBFReader
			DBFReader reader = new DBFReader(inputStream, StandardCharsets.UTF_8);

			// 遍历行
			Object[] rowObjects;
			StringBuilder rowJSON = null;
			while ((rowObjects = reader.nextRecord()) != null) {
				// 将每行的数据拼接成 JSON 串
				rowJSON = new StringBuilder();
				rowJSON.append("{");
				for (int i = 0; i < rowObjects.length; i++) {
					// 获取表头
					String title = reader.getField(i).getName();
					if (UtilValidate.isEmpty(title)) {
						continue;
					}

					// 从映射关系中获取当前表头对应的字段名称, 若没有, 则使用表头名称
					String field = columnMap.get(title);
					if (UtilValidate.isNotEmpty(field)) {
						title = field;
					}

					// 将表头名拼接进去, 拼成 JSON
					rowJSON.append(JSON.toJSONString(title))
							.append(":")
							.append(JSON.toJSONString(rowObjects[i]))
							.append(",");
				}
				// 去除 JSON 最后多余的逗号, 并加上结尾的右大括号
				rowJSON.deleteCharAt(rowJSON.length() - 1);
				rowJSON.append("}");

				T rowObject = JSONObject.parseObject(rowJSON.toString(), clazz);
				result.add(rowObject);
			}
		} finally {
			inputStream.close();
		}
		return result;
	}

	/**
	 * 导出 DBF 文件的基本方法
	 *
	 * @param param 入参对象
	 * @param clazz 使用对象列表为入参时, 需要指明对象的 Class
	 * @return 操作结果
	 * @throws Exception 读取文件、获取输出流、关闭流的相关异常
	 */
	public static <T> String commonExportDbf(DbfParam param, Class<T> clazz) throws Exception {
		String result = "-";

		// 初始化文件名称
		String fileName = param.getFileName();
		if (UtilValidate.isEmpty(fileName)) {
			fileName = String.valueOf(new Date().getTime());
		}
		if (!fileName.contains(".dbf")) {
			fileName = fileName + ".dbf";
		}

		List<DBFField> fieldList = param.getFieldList();
		List<Object[]> rowData = param.getRowData();
		if (param.getIsEntity()) {
			// 入参传的是 对象数组
			// 获取 表头名称 和 字段名称 映射关系
			createDBFInfoByDataList(param, clazz, rowData, fieldList);
		}

		// 创建输出流
		OutputStream outputStream = null;
		if (!param.getIsStream()) {
			String filePath = param.getFilePath();
			if (!filePath.contains(".dbf")) {
				filePath = filePath + fileName;
			}
			outputStream = new FileOutputStream(filePath);
		} else {
			outputStream = param.getResponse().getOutputStream();
		}
		// 创建输出对象, 且设置字符编码
		DBFWriter writer = new DBFWriter(outputStream, StandardCharsets.UTF_8);
		try {
			// 插入字段信息
			DBFField[] fields = new DBFField[fieldList.size()];
			writer.setFields(fieldList.toArray(fields));

			// 插入数据信息
			for (Object[] rowDatum : rowData) {
				writer.addRecord(rowDatum);
			}

			// 如果使用 HTTP 响应, 构造响应配置
			if (param.getIsStream()) {
				param.getResponse().reset();
				param.getResponse().setHeader("Content-disposition",
						"attachment; filename=" + new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8));
				param.getResponse().setContentType("application/x-download");
			}
			result = "导出 DBF 成功";
		} catch (Exception e) {
			log.error("×××× commonExportDbf ×× {} ××××××××××\n  ", e.getMessage(), e);
			result = "导出 DBF 异常";
		} finally {
			writer.close();
			outputStream.flush();
			outputStream.close();
		}
		return result;
	}

	/**
	 * 通过 对象数组 和 对象 构造生成 DBF 文件需要的配置信息以及数据信息
	 *
	 * @param param     入参对象
	 * @param clazz     对象 Class
	 * @param rowData   构造完成后的行数据信息(作返回值)
	 * @param fieldList 构造完成后的字段配置信息(作返回值)
	 */
	public static <T> void createDBFInfoByDataList(DbfParam param, Class<T> clazz, List<Object[]> rowData, List<DBFField> fieldList) {
		Map<String, String> columnMap = param.getColumnMap(); // 获取输入的字段名与表头的映射关系

		// 若映射关系为空, 按照对象生成
		if (UtilValidate.isEmpty(columnMap)) {
			columnMap = getColumnMap(clazz, 2);
		}

		// 获取所有需要生成进 DBF 的字段名称
		Set<String> fieldSet = columnMap.keySet();
		ArrayList<String> fieldNameList = new ArrayList<>(fieldSet);
		// 遍历所有需要字段信息, 初始化字段信息
		DBFField dbfField = null;

		// 用于存放 DBF 字段名与字段信息 Map
		Map<String, DBFField> DBFFieldMap = getDBFFieldByAnnotation(clazz);

		// 遍历所有传入的对象列表
		List dataList = param.getDataList();
		int size = fieldNameList.size();
		Object[] rowInfo = null;
		for (Object data : dataList) {
			// 获取当前记录信息
			//String string = JSON.toJSONStringWithDateFormat(, "yyyy-MM-dd HH:mm:ss", SerializerFeature.WriteDateUseDateFormat);
			JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(data));
			// 遍历每个字段
			rowInfo = new Object[size];
			for (int i = 0; i < size; i++) {
				String fieldName = fieldNameList.get(i);
				dbfField = DBFFieldMap.get(fieldName);
				// 获取字段值
				Object fieldValue = jsonObject.get(fieldName);
				String fieldValueStr = String.valueOf(fieldValue);
				// 如果当前字段值长度, 比已保存的字段长度要长, 则替换原来长度
				DBFDataType type = dbfField.getType();

				// 当前数据类型最大长度 ; 当前配置的长度 ; 当前字段值的长度
				int maxSize = type.getMaxSize(), length = dbfField.getLength(), tempLength = fieldValueStr.length();
				if (tempLength > length && UtilValidate.isEmpty(maxSize)) {
					dbfField.setLength(tempLength);
				}

				// 如果有小数, 则更新小数位数
				if (Objects.equals(type, DBFDataType.NUMERIC)) {
					String[] split = fieldValueStr.split("\\.");
					if (split.length >= 2) {
						int tempDecimalCount = split[1].length();
						int decimalCount = dbfField.getDecimalCount();
						if (tempDecimalCount > decimalCount) {
							dbfField.setDecimalCount(tempDecimalCount + 1);
						}
					}
				}
				// 如果有日期型, 格式化日期格式
				DBFFieldMap.put(fieldName, dbfField);
				rowInfo[i] = fieldValue;
				if (Objects.equals(type, DBFDataType.DATE)) {
					if (fieldValueStr.contains("-")) {
						rowInfo[i] = null;
					} else {
						rowInfo[i] = new Date(Long.parseLong(fieldValueStr));
					}
				}
			}
			rowData.add(rowInfo);
		}

		for (String fieldName : fieldSet) {
			dbfField = DBFFieldMap.get(fieldName);
			fieldList.add(dbfField);
		}
	}


	/**
	 * 获取对象中 字段名称 和 表头名称(@DBF) 映射关系
	 *
	 * @param clazz   实体类对象
	 * @param keyFlag 生成的 Map 是以哪个为 Key, (1 以表头为Key ; 2 以字段名为 Key)
	 * @return Map<\ 表头名称, 字段名称>
	 */
	public static <T> Map<String, String> getColumnMap(Class<T> clazz, int keyFlag) {
		Map<String, String> map = new HashMap<>(20);
		try {
			if (UtilValidate.isEmpty(clazz)) {
				return map;
			}

			Field field;
			Field[] fields = clazz.getDeclaredFields();
			for (Field value : fields) {
				value.setAccessible(true);
			}
			for (Field value : fields) {
				field = clazz.getDeclaredField(value.getName());
				DBF column = field.getAnnotation(DBF.class);
				// 判断是否忽略该字段
				if (column == null || column.ignore()) {
					continue;
				}
				if (keyFlag == 1) {
					map.put(column.name(), field.getName());
				} else {
					map.put(field.getName(), column.name());
				}
			}
		} catch (Exception e) {
			log.error("×××× getColumnMap ×× {} ××××××××××\n  ", e.getMessage(), e);
		}
		return map;
	}

	/**
	 * 获取对象中 字段名称 和 表头名称(@DBF) 映射关系
	 *
	 * @param clazz 实体类对象
	 * @return Map<\ 表头名称, 字段名称>
	 */
	public static <T> Map<String, DBFField> getDBFFieldByAnnotation(Class<T> clazz) {
		Map<String, DBFField> result = new HashMap<>();
		try {
			if (UtilValidate.isEmpty(clazz)) {
				return result;
			}

			DBFField dbfField = null;
			Field field = null;
			Field[] fields = clazz.getDeclaredFields();
			for (Field value : fields) {
				value.setAccessible(true);
			}
			for (Field value : fields) {
				field = clazz.getDeclaredField(value.getName());
				DBF annotation = field.getAnnotation(DBF.class);
				// 判断是否忽略该字段
				if (annotation == null || annotation.ignore()) {
					continue;
				}
				// 解析注解信息
				String name = annotation.name();
				DBFDataType type = annotation.type();
				int length = annotation.length();
				int decimalCount = annotation.decimalCount();
				// 校验是否为空, 若空则给默认值
				if (Objects.equals(name, "")) {
					name = field.getName();
				}
				if (name.length() > 10) {
					name = getInitialFromWords(name);
				}
				if (type == DBFDataType.UNKNOWN) {
					type = getDBFDataTypeByJavaDataName(field.getType().getSimpleName());
				}
				int maxSize = type.getMaxSize();
				if (length <= 0 || (maxSize > 0 && length > maxSize)) {
					length = maxSize;
				}
				// 创建字段对象
				if (type == DBFDataType.NUMERIC) {
					dbfField = new DBFField(name, type, length, decimalCount);
				} else {
					dbfField = new DBFField(name, type, length);
				}

				result.put(field.getName(), dbfField);
			}
		} catch (Exception e) {
			log.error("×××× getDBFAnnotationInfo ×× {} ××××××××××\n  ", e.getMessage(), e);
		}
		return result;
	}

	/**
	 * 获取 Java 数据类型 对应的 DBF 数据类型
	 *
	 * @param JavaDataName java 数据类型名称
	 * @return DBF 数据类型
	 */
	public static DBFDataType getDBFDataTypeByJavaDataName(String JavaDataName) {
		DBFDataType result = DBFDataType.UNKNOWN;

		switch (JavaDataName) {
			case "String":
			case "Character":
			case "char":
				result = DBFDataType.CHARACTER;
				break;
			case "Date":
				result = DBFDataType.DATE;
				break;
			case "Double":
			case "double":
				result = DBFDataType.DOUBLE;
				break;
			case "Float":
			case "float":
				result = DBFDataType.FLOATING_POINT;
				break;
			case "Boolean":
			case "boolean":
				result = DBFDataType.LOGICAL;
				break;
			case "Byte":
			case "byte":
			case "Byte[]":
			case "byte[]":
				result = DBFDataType.BLOB;
				break;
			case "Long":
			case "long":
				result = DBFDataType.LONG;
				break;
			case "Integer":
			case "int":
			case "BigDecimal":
				result = DBFDataType.NUMERIC;
				break;
			default:
				break;
		}
		return result;
	}

	/**
	 * 获取 驼峰格式 / 下划线形式 的字符串中的首字母
	 *
	 * @param words 待处理字符串
	 * @return 处理完成的字符串
	 */
	public static String getInitialFromWords(String words) {
		StringBuilder upperWords = new StringBuilder();

		String regex = "_";
		if (words.contains(regex)) {
			// 按照下划线格式解析
			String[] wordList = words.split(regex);
			for (String word : wordList) {
				upperWords.append(word.charAt(0));
			}
		} else {
			upperWords.append(words.charAt(0));
			// 按照驼峰格式解析
			for (int i = 0; i < words.length(); ++i) {
				if (Character.isUpperCase(words.charAt(i))) {
					upperWords.append(words.charAt(i));
				}
			}
		}
		return upperWords.toString().toUpperCase();
	}

	/**
	 * 将Map格式的字符串转成 Map 对象
	 */
	public static Map<String, String> getMap(String keyValue) {
		Map<String, String> map = new HashMap<String, String>(20);
		if (keyValue != null) {
			String[] str = keyValue.split(",");
			for (String element : str) {
				String[] str2 = element.split(":");
				map.put(str2[0].trim(), str2[1].trim());
			}
		}
		return map;
	}

}
